Most common for SPAs (like your React frontend)
Microsoft.AspNetCore.Authentication.JwtBearer
.Setup Highlights:
Authorization
header (Bearer <token>
).Role support:
"roles": ["Admin", "User"]
).[Authorize(Roles = "Admin")]
.Traditional approach for server-rendered apps (not ideal for APIs)
Best for federated login, single sign-on (SSO), or external providers
Microsoft.AspNetCore.Authentication.OpenIdConnect
Role support:
[Authorize(Roles = "Admin")]
still works.Lightweight alternative (not ideal for user-based roles)
username:password
in Base64 via Authorization
header.Full-featured user management system (often combined with JWT)
UserManager
, RoleManager
.Example: Use Identity for creating users and roles, then issue JWTs on login.
Method | Stateless | Token-Based | Role Support | Ideal For |
---|---|---|---|---|
JWT Authentication | β | β | β | APIs, SPAs (React, etc.) |
Cookie Authentication | β | β | β | Server-side apps only |
OAuth2 + OpenID Connect | β | β | β | External login, SSO, enterprise |
API Key | β | β | β (manual) | Simple apps, service-to-service |
Basic Auth | β | β | β (manual) | Very basic use, not recommended |
ASP.NET Core Identity | β | Optional | β | User/Role management |
Definition:
A browser security mechanism that helps prevent XSS attacks by controlling which sources the browser can load content from.
Example Usage:
It can prevent JavaScript from running unless it's from a trusted source.
Example CSP header:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trustedscripts.example.com
Definition:
A vulnerability where an attacker injects malicious JavaScript into a web page that gets executed in a userβs browser.
Why It Matters for JWT:
If you store JWTs in localStorage
, and your app is vulnerable to XSS, the attacker can steal the token and impersonate the user.
Prevention:
Definition:
The middle part of a JWT. It contains the claims (user data, permissions, roles, etc.) in a Base64-encoded JSON format.
Example:
{
"sub": "1234567890",
"name": "John Doe",
"role": "Admin",
"exp": 1716850984
}
β οΈ Note: Payload is not encrypted, just encoded β anyone can read it, but not modify it without invalidating the signature.
Definition:
The third part of the token. It's a cryptographic hash (HMAC or RSA/ECDSA) of the header and payload, signed with a secret or private key.
Purpose:
To verify that the token hasnβt been tampered with.
Structure:
JWT = base64(header) + '.' + base64(payload) + '.' + signature
Only the server (with the secret key) can validate the signature.
Definition:
A vulnerability where an attacker tricks a userβs browser (with a valid session/cookie) into making an unwanted request to your site without the userβs knowledge.
Example:
If you're logged in and visit a malicious site, that site may submit a POST request using your cookies to perform actions on your behalf.
Why It Matters:
Defense:
SameSite=Strict
cookiesAuthorization
header with tokens (doesnβt auto-send like cookies)Definition:
The memory and server resource cost of maintaining a session for each logged-in user on the server.
Why It Matters:
Definition:
Users log in to your application using another trusted identity provider (IdP), such as:
You delegate authentication to the third party and just receive user info (often as a JWT or OpenID Connect token).
Protocol Examples:
Definition:
A user logs in once and gains access to multiple systems or applications without logging in again.
Common in Enterprises:
How It Works:
Term | Meaning |
---|---|
CSP | Restricts sources of scripts/styles to prevent XSS |
XSS | Injected JavaScript that steals data like JWT tokens |
JWT Payload | JSON with user claims (readable but not secure on its own) |
JWT Signature | Proves the token is untampered (signed by server) |
CSRF | Unauthorized actions using authenticated user's browser/session |
Session Overhead | Cost of storing user sessions in memory on the server |
Federated Login | Login using a third-party identity provider (e.g., Google) |
SSO | One login grants access to multiple trusted systems |
ASP.NET Identity is a full membership system that:
Default storage: SQL Server (via Entity Framework)
β
Stateful
β Doesn't scale well for large APIs unless you add external session storage (like Redis).
JWT Authentication is:
Authorization
header.β
Stateless
β
Scales easily
β
Good for distributed APIs
Feature | ASP.NET Identity (Cookie) | JWT Authentication (Token-Based) |
---|---|---|
Stateful/Stateless | Stateful | Stateless |
Storage | Cookie on client, session on server | Token on client only |
Default Transport | Cookie (auto-sent by browser) | Authorization header (manual send) |
Built-in Support | ASP.NET Identity (UI + EF Core) | ASP.NET Core + Manual JWT setup |
Scalability | Limited (server stores session) | High (no session to manage) |
Security | Cookie CSRF risk | XSS risk if stored in JS-accessible storage |
Use Case | Web apps with UI (MVC, Razor) | APIs, SPAs, mobile apps |
External login support | Built-in | Needs integration |
Token expiration | Server-controlled session | Token has expiration embedded |
β οΈ You can't "force logout" someone server-side in pure JWT unless you blacklist tokens manually (or rotate secrets).
Yes β ASP.NET Identity can be configured to:
This is often used in hybrid apps:
null!
mean?It's a null-forgiving operator.
When you write:
string? maybeNull = null;
string definitelyNotNull = null!; // β This tells the compiler: βTrust me, itβs not null.β
You're telling the compiler:
βI know this is technically null, but treat it as non-null and donβt warn me.β
C# 8 introduced nullable reference types, where the compiler gives warnings if you assign or use null
where it's not safe.
For example:
string name = null; // Warning: assigning null to non-nullable reference
To silence the warning, you can use null!
:
string name = null!; // No warning
But this is your responsibility: the compiler assumes you're guaranteeing that it wonβt cause a NullReferenceException
.
null!
?Use it sparingly and carefully, only when:
public class Account
{
public string PhoneNumber { get; set; } = null!; // EF will populate it
}
Expression | Meaning |
---|---|
null! |
"This is null, but treat it as if it's non-null (trust me)." |
Use case | Avoiding compiler warnings for nullable reference types |
Risk | Can lead to runtime NullReferenceException if used incorrectly |
Let me know if you want help turning on/off nullable reference types or dealing with EF Core model warnings.
A JWT Generator is a class or service in your backend that:
A JWT has 3 parts:
xxxxx.yyyyy.zzzzz
Header (Base64-encoded JSON):
{
"alg": "HS256",
"typ": "JWT"
}
Payload (Base64-encoded JSON):
Contains user data and claims (e.g., user ID, role, expiry).
{
"sub": "userId123",
"phone": "0930xxx",
"role": "Admin",
"exp": 1717502800
}
Signature:
HMACSHA256(header + "." + payload, secret key)
Include things you want to check without querying the DB every time:
sub
(Subject β usually user ID)phone
or usernamerole
(e.g., "Admin", "User")exp
(expiration timestamp)companyId
, verified
, etc.The signature ensures that the token hasn't been tampered with. If the signature doesn't match (due to modification or incorrect secret), the token is invalid.
public interface IJwtGenerator
{
string GenerateToken(Account account);
}
public class JwtGenerator : IJwtGenerator
{
private readonly IConfiguration _config;
public JwtGenerator(IConfiguration config)
{
_config = config;
}
public string GenerateToken(Account account)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()),
new Claim(JwtRegisteredClaimNames.PhoneNumber, account.PhoneNumber),
new Claim(ClaimTypes.Role, account.Role),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]!));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddHours(2),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
appsettings.json
"Jwt": {
"Key": "YourSuperSecureSecretKey123!",
"Issuer": "YourAppName",
"Audience": "YourFrontendApp"
}
When the frontend sends this token on requests (via Authorization: Bearer <token>
header), ASP.NET Core automatically validates:
exp
)This is done via JwtBearer
middleware.
Yes, you can (and should) include user roles. The backend will automatically enforce [Authorize(Roles = "Admin")]
using that claim.
But:
No, standard practice is:
localStorage
with care).Responsibility | Backend (JWT Generator) | Frontend |
---|---|---|
Token generation | β | β |
Storing token | β | β (localStorage / cookie) |
Sending token | β | β (Authorization header) |
Token validation | β
(JwtBearer ) |
β |
Role enforcement | β
([Authorize] ) |
β (UI-level only) |
"role"
or ClaimTypes.Role
.Example:
{
"sub": "1234567890",
"phone_number": "123456789",
"role": "Admin",
"role": "Editor",
"role": "User",
"exp": 1711600000
}
"role"
entries.ClaimsPrincipal
reads all of them, so [Authorize(Roles="Admin,Editor")]
works by checking if any of the roles match.jwt-decode
).Example in React using jwt-decode
:
import jwtDecode from 'jwt-decode';
const token = localStorage.getItem('token');
const decoded = jwtDecode(token);
const roles = decoded.role; // roles is usually an array if multiple roles exist
console.log(roles); // ["Admin", "Editor", "User"]
The frontend can then use these roles to:
[Authorize]
attributes or middleware.Step | How it works |
---|---|
JWT creation | Multiple "role" claims added to token |
Backend authorization | Validates token and checks if user has required roles |
Frontend decoding | Extracts role claim(s) as array of strings |
Frontend UI control | Shows or hides content based on roles |